Foreword
解决问题的前提是定位问题. 在掌握这些工具之后, 才发现之前排查问题的方式简直是隔靴搔痒. 为了区别系统级的命令, 把本章取名为”Java诊断工具”.
Greys Anatomy
一次在排查代码运行性能过程中, 尝试编写一个BTrace
脚本来跟踪VM栈每一步调用的耗时(期望效果类似Pinpoint的调用跟踪), 然而这个脚本憋了半天也没写出来. 终于遇到了阿里的Greys Anatomy - Github, 不禁惊叹这个工具真是太方便了, 只要三步:
1 | jps -ml // 确定进程ID |
这个项目的文档非常全面而且是中文, 可以直接点击上面的链接深入了解.
JVM自带工具
本章的内容基于JDK8, 不同版本输出的内容有些差别. 相关内容在Java Platform, Standard Edition Tools Reference for Oracle JDK on Solaris, Linux, and OS X, Release 8写的很详细.
- JDK基本工具类位于
$JAVA_HOME/bin
. - 这些工具类的实现基于
tools.jar
, 它不是Java标准API, HotSpot提供的. 所以能开箱即用的只有HotSpot虚拟机. - 部分JDK小工具参考了UNIX命名方式, 如
jps
和ps
功能类似. - 如果对命令的选项不清楚, 直接执行
命令名 -help
即可查询.
1. jps
列出正在运行的虚拟机进程. jps [options] [hostid]
- -m : 输出传递给main函数的参数
- -l : 输出主类的全名/jar包路径
- -v : 输出JVM参数
2. jstat
这个命令功能比较多, 根据选项可以对一个JVM进程某个特定指标进行查询. 这里展示的结果项非常多,
比如查看GC情况
1 | jstac -gc 5234 // 查看进程好5234的GC情况 |
观察结果, 截止到CCSU
之前都可用下面两条组合解释:
S0
,S1
表示新生代中的两个Survivor区域. E表示Eden. O表示Old Gen. M表示MetaSpace. CCS表示压缩类空间(Compressed Class Space), 它是MetaSpace的一部分.- C即Capacity, 表示容量. U为USED, 表示已用多少. 单位都是KB.
CCSU
后面的含义如下;
- YGC : 年轻代GC次数
- YGCT : 年轻代GC时间
- FGC : 年老代GC次数
- FGCT : 年老带GC时间
- GCT : GC消耗总时间
jstat
的基本格式为: jstat [option vmid [interval[s|ms]] [count]]
.
刚刚的option
是-gc
. interval
和count
分别表示: 每隔interval
执行一次, 共执行count
次后结束. jstat -gc 5234 2s 3
, 即没两秒执行一次, 执行3次后结束. 除了-gc
之外还支持:
-class
1 | stat -class 5234 |
依次表示 加载的类数量, 字节数, 卸载的类数量, 卸载的类字节数. 加载类耗费的时间.
-complier
1 | jstat -compiler 5234 |
-printcompilation
1 | jstat -printcompilation 5234 |
其他GC相关
-gccapacity
1 | jstat -gccapacity 5234 |
观察后缀, 主要多了MN
, MX
, 同样指的是Capacity
,分别表示最小, 最大.
-gcutil
1 | jstat -gcutil 5234 |
以百分比格式输出占用量.
-gcnew, -gcold, -gcnewcapacity, -gcoldcapacity
功能应该可以猜到了.1
2
3
4
5
6
7jstat -gcold 5234
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
62688.0 61132.7 7412.0 7099.1 204800.0 32791.2 10 4 0.261 2.087
jstat -gcnew 5234
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
61440.0 61440.0 10961.4 0.0 6 6 30720.0 491520.0 46711.6 10 1.826
这里有点奇怪的是, 为什么MetaSpace归属到Old Gen的统计中了? 另外年轻代有三个之前没有看过的指标: TT
, MTT
, DSS
TT: Tenuring threshold.
MTT: Maximum tenuring threshold.
DSS: Desired survivor size (kB).
-gccause
1 | jstat -gccause 5234 |
同样以百分比输出, 多了个LGCC
, 即 Last GC Cause. GCC
, 表示当前GC原因.
3. jinfo
查看参数信息和System Properties. 其中有很多重要信息, 包括一些默认的JVM参数. 除次之外很多框架都将属性System.setProperties
, 可以直接通过此命令查看.
1 | jinfo 5234 |
选项 :
-flag name : Prints the name and value of the specified command-line flag.
-flag [+|-]name : enables or disables the specified Boolean command-line flag.
-flag name=value : Sets the specified command-line flag to the specified value.
-flags : Prints command-line flags passed to the JVM.
-sysprops : Prints Java system properties as name-value pairs.
4. jmap
功能为dump出堆内存情况. 先看如何使用
1 | jmap -dump:format=b,file=test.dump 5234 |
把PID为5234的虚拟机进程堆内存情况dump到test.dump这个文件中.
如果指定live选项jmap -dump:live,for...
则表示只dumo对中活跃的对象. 注意结果文件不是人类可读的, 需要通过工具如jhat
或者VisualVM这种工具来. 一般不使用jhat
, 就不介绍了. 通过VisualVM 1.4.1, File -> Load, 选择文件即可.
dump
是最常用的, 除此之外还支持:
- 没有任何选项. // TODO
- finalizerInfo: 打印F-Queue中等待Finalizer线程执行finalize方法的对象.
- F : 当使用
-dump
,-histo
选项时, 如果虚拟机没有反应, 可以加此选项强制. 但不能和live子选项共同使用.
-Heap
显示堆的摘要信息.
1 | jmap -heap 5234 |
可以看到其中包含一些GC信息, 以及Interned String的数量和存储容量.
-histo (-histo:live)
1 | jmap -histo 5234 >> test.histo |
显示堆中每个类的实例数量, 和所占容量大小. 也支持live选项, 含义和-dump
中一样, 也表示仅仅统计活跃的对象.
-clstats
打印堆中的ClassLoader统计. 包括名称, 活跃度, 地址, 父级ClassLoader, 加载的Class数量等信息.
5. jstack
线程信息, 支持选项
- -F : 当VM hang住时使用此选项.
- -l : 显示锁信息
- -m : 如果有调用Native方法, 显示其(C/C++)栈信息()
VisualVM
VisualVM的功能实际上是几个JDK命令的集合. 一个问题是VisualVM通常不会安装在服务端, 所以需要Server开启JMX端口, 才可以被监控. 需要添加的JVM参数如下:
- -Dcom.sun.management.jmxremote.port=9876 // 为VM实例指定JMX端口
- -Dcom.sun.management.jmxremote.ssl=false // 禁用ssl
- -Dcom.sun.management.jmxremote.authenticate=false // 禁用授权校验 (用户名密码)
- -Djava.rmi.server.hostname=10.1.5.54 // 本机IP